#!/bin/sh
# arguments :
# 1) minidump file (*.dmp)
# 2) symbol files directory; on Windows, path where *.pdb files are, on Linux path where binaries are
# example  : qcrashdumper mydumpfile.dmp ~/Desktop/Repos/binaries

# get args

dmp_file=$1
bin_dir=$2

# check running machine
uname_out="$(uname -s)"
case "${uname_out}" in
    Linux*)     machine=Linux;;
    Darwin*)    machine=Mac;;
    CYGWIN*)    machine=Win;;
    MINGW*)     machine=Win;;
	MSYS*)      machine=Win;;
    *)          machine="UNKNOWN:${uname_os}"
esac
#echo "[INFO] machine is ${machine}."

# check arg
if [[ ! "${dmp_file}" ]]; then
	printf "\n[ERROR] missing first argument; provide minidump file (*.dmp)\n\n" >&2; 
	exit 1; 
fi
if [[ ! -d "${bin_dir}" ]]; then
	if [[ $machine == "Win" ]]; then
		printf "\n[ERROR] missing second argument; provide symbols files path (*.pdb)\n\n" >&2; 
	else
		printf "\n[ERROR] missing second argument; provide symbols files path\n\n" >&2; 
	fi
	exit 1; 
fi

# try to add relevant paths
scr_dir="$(dirname "$(readlink -f "$0")")"
if ! type "dump_syms" > /dev/null 2>&1; then
	if [[ $machine == "Win" ]]; then
		PATH="${PATH}:${scr_dir}/../deps/breakpad.git/src/tools/windows/binaries"
	else
		PATH="${PATH}:${scr_dir}/../deps/breakpad.git/src/tools/linux/dump_syms"
	fi
fi
if ! type "minidump_stackwalk" > /dev/null 2>&1; then
	PATH="${PATH}:${scr_dir}/../deps/breakpad.git/src/processor"
fi

# check for dependencies
if ! type "dump_syms" > /dev/null 2>&1; then
	echo "[ERROR] dump_syms not found in PATH."
	exit 1
fi
if ! type "minidump_stackwalk" > /dev/null 2>&1; then
	echo "[ERROR] minidump_stackwalk not found in PATH."
	exit 1
fi

# clear any previous symbols dir
if [[ -d "symbols" ]]; then
	rm -rf "symbols"
fi

# find out which dependencies minidump_stackwalk is expecting
sym_marker="No symbol file at "
IFS=$'\n'
list_lines=( $(minidump_stackwalk "${dmp_file}" symbols 2>&1 | grep "${sym_marker}") )
for f in ${!list_lines[@]}; do
    curr_line=${list_lines[$f]}
    # remove up to marker
    curr_line="${curr_line#*${sym_marker}}"
    # overwrite array entry
    list_lines[$f]=${curr_line}
    IFS=$'/'
    list_parts=( ${curr_line} )
    # read parts
    sym_dir=${list_parts[0]}
    bin_file=${list_parts[1]}
    uniq_dir=${list_parts[2]}
    sym_file=${list_parts[3]}
    # check if binary dependency exists in given binary path
    bin_filepath=${bin_dir}/${bin_file}
    # on linux, debug symbols might be in the binary itself or in the file pointed by the gnu_debuglink stored in the binary file
    if [[ $machine == "Linux" ]]; then
    	# read gnu_debuglink
		sym_file=$(readelf --string-dump=.gnu_debuglink "${bin_filepath}" | sed -n '/]/{s/.* //;p;q}')
		# if gnu_debuglink file exists, then use pointed file to extract symbols instead
		if [[ ! -e ${sym_file} ]]; then
			sym_file=${bin_dir}/${sym_file}
		fi
		if [ -f "${sym_file}" ]; then
		    bin_filepath="${sym_file}"
		fi
	fi
    if [[ ! -e ${bin_filepath} ]]; then
    	echo "[WARN] ${bin_file} not found in given path, could not create ${sym_file}."
	    continue
	fi
	# create target symbols dir
	mkdir -p "${sym_dir}"
	mkdir -p "${sym_dir}/${bin_file}"
	mkdir -p "${sym_dir}/${bin_file}/${uniq_dir}"
	dump_syms "${bin_filepath}" > ${curr_line}
	echo "[INFO] ${curr_line} created successfully."
done

# get analysis and put in file
dmp_basefile=$(basename "${dmp_file}")
minidump_stackwalk "${dmp_file}" symbols > ${dmp_basefile}.txt  2>&1

# print 50 lines after "Crash" match
awk '/Crash/ {for(i=1; i<=50; i++) {getline; print}}' ${dmp_basefile}.txt
echo ""
echo "[INFO] full results written down to ${dmp_basefile}.txt."